package org.xmppbot.net;

import static org.junit.Assert.*;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.security.auth.callback.*;
import javax.security.sasl.*;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * Проверка работы Java SASL и сравнение с примером http://xmpp.org/rfcs/rfc3920.html#sasl.
 * User: Bulat Nigmatullin
 * Date: 16.07.2009
 * Time: 8:23:52
 */
public class SASLCheck {

    private static final Logger logger = LoggerFactory.getLogger(SASLCheck.class);

    private static String authorizationId = "example.org";
    private static String password = "testPassword";
    private static String protocol = "xmpp";
    private static final String serverName = "somerealm";

    Map<String, String> props = new HashMap<String, String>();

    {
        props.put(Sasl.QOP, "auth");
    }

    @Test
    public void digestmd5() throws Exception {
        String mechanism = "DIGEST-MD5";
        SaslClient sc = Sasl.createSaslClient(new String[]{mechanism},
                authorizationId, protocol, serverName, props, new ClientCallbackHandler());

        SaslServer ss = Sasl.createSaslServer(mechanism, "xmpp", serverName, props, new ServerCallbackHandler());
        byte[] token = new byte[0];
        byte[] challenge = ss.evaluateResponse(token);
        System.out.println(new String(challenge));
        logger.debug("---------------------------------------");
        byte[] response = sc.evaluateChallenge(challenge);
        System.out.println(new String(response));
        logger.debug("---------------------------------------");
        byte[] challenge2 = ss.evaluateResponse(response);
        System.out.println(new String(challenge2));
        logger.debug("---------------------------------------");
        byte[] response2 = sc.evaluateChallenge(challenge2);
        assertNull(response2);
    }

    private String getPassword(String name) {
        if (authorizationId.equals(name)) {
            return password;
        } else {
            return "";
        }
    }

    private class ClientCallbackHandler implements CallbackHandler {

        @Override
        public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
            for (Callback callback : callbacks) {
                logger.debug("ClientCallback: {}", callback);
                if (callback instanceof RealmCallback) {
                    RealmCallback realmCallback = (RealmCallback) callback;
                    logger.debug("RealmCallback: {}, {}, {}", new Object[]{realmCallback.getDefaultText(), realmCallback.getPrompt(), realmCallback.getText()});
                    realmCallback.setText(realmCallback.getDefaultText());
                } else if (callback instanceof NameCallback) {
                    NameCallback nameCallback = (NameCallback) callback;
                    logger.debug("NameCallback: {} {} {}", new Object[]{nameCallback.getDefaultName(), nameCallback.getPrompt(), nameCallback.getName()});
                    nameCallback.setName(nameCallback.getDefaultName());
                } else if (callback instanceof PasswordCallback) {
                    PasswordCallback passwordCallback = (PasswordCallback) callback;
                    logger.debug("PasswordCallback: {} {}", passwordCallback.getPassword(), passwordCallback.getPrompt());
                    passwordCallback.setPassword("testPassword".toCharArray());
                }
            }
        }
    }

    private class ServerCallbackHandler implements CallbackHandler {

        @Override
        public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
            String realm;
            String name = null;
            for (Callback callback : callbacks) {
                logger.debug("ServerCallback: {}", callback);
                if (callback instanceof RealmCallback) {
                    RealmCallback realmCallback = (RealmCallback) callback;
                    logger.debug("RealmCallback: {}, {}, {}", new Object[]{realmCallback.getDefaultText(), realmCallback.getPrompt(), realmCallback.getText()});
                    realm = realmCallback.getText();
                } else if (callback instanceof NameCallback) {
                    NameCallback nameCallback = (NameCallback) callback;
                    logger.debug("NameCallback: {} {} {}", new Object[]{nameCallback.getDefaultName(), nameCallback.getPrompt(), nameCallback.getName()});
                    name = nameCallback.getName();
                    if (name == null) {
                        name = nameCallback.getDefaultName();
                    }
                } else if (callback instanceof PasswordCallback) {
                    PasswordCallback passwordCallback = (PasswordCallback) callback;
                    logger.debug("PasswordCallback: {} {}", passwordCallback.getPassword(), passwordCallback.getPrompt());
                    passwordCallback.setPassword(getPassword(name).toCharArray());
                } else if (callback instanceof AuthorizeCallback) {
                    AuthorizeCallback authCallback = (AuthorizeCallback) callback;
                    logger.debug("AuthorizeCallback: {} {}", authCallback.getAuthenticationID(), authCallback.getAuthorizationID());
                    authCallback.setAuthorized(true);
                    authCallback.setAuthorizedID(authCallback.getAuthorizationID());
                }
            }
        }
    }

}
